﻿// Type: System.ServiceModel.DomainServices.Client.EntityCollection`1
// Assembly: System.ServiceModel.DomainServices.Client, Version=2.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// Assembly location: C:\Program Files (x86)\Microsoft SDKs\RIA Services\v1.0\Libraries\Silverlight\System.ServiceModel.DomainServices.Client.dll

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Windows.Data;

namespace System.ServiceModel.DomainServices.Client
{
  public sealed class EntityCollection<TEntity> : IEntityCollection, IEnumerable<TEntity>, IEnumerable, INotifyCollectionChanged, INotifyPropertyChanged, ICollectionViewFactory where TEntity : Entity
  {
    private Action<TEntity> _attachAction;
    private Action<TEntity> _detachAction;
    private AssociationAttribute _assocAttribute;
    private Entity _parent;
    private EntitySet _sourceSet;
    private Func<TEntity, bool> _entityPredicate;
    private List<TEntity> _entities;
    private NotifyCollectionChangedEventHandler _collectionChangedEventHandler;
    private PropertyChangedEventHandler _propertyChangedEventHandler;
    private TEntity _attachingEntity;
    private TEntity _detachingEntity;
    private bool _entitiesLoaded;
    private bool _entitiesAdded;
    private bool _isComposition;
    private EventHandler<EntityCollectionChangedEventArgs<TEntity>> EntityAdded;
    private EventHandler<EntityCollectionChangedEventArgs<TEntity>> EntityRemoved;

    List<TEntity> Entities
    {
      private get
      {
        if (this._entities == null)
          this._entities = new List<TEntity>();
        return this._entities;
      }
    }

    public int Count
    {
      get
      {
        this.Load();
        return this.Entities.Count;
      }
    }

    bool IsSourceExternal
    {
      private get
      {
        if (this.SourceSet != null)
          return this.SourceSet.EntityContainer != this._parent.EntitySet.EntityContainer;
        else
          return false;
      }
    }

    EntitySet SourceSet
    {
      private get
      {
        if (this._parent.EntitySet != null)
          this._sourceSet = this._parent.EntitySet.EntityContainer.GetEntitySet(typeof (TEntity));
        return this._sourceSet;
      }
    }

    AssociationAttribute IEntityCollection.Association
    {
      get
      {
        return this._assocAttribute;
      }
    }

    bool IEntityCollection.HasValues
    {
      get
      {
        if (this._entities != null)
          return this._entities.Count > 0;
        else
          return false;
      }
    }

    IEnumerable<Entity> IEntityCollection.Entities
    {
      get
      {
        return Enumerable.Cast<Entity>((IEnumerable) this);
      }
    }

    public event EventHandler<EntityCollectionChangedEventArgs<TEntity>> EntityAdded
    {
      add
      {
        EventHandler<EntityCollectionChangedEventArgs<TEntity>> eventHandler = this.EntityAdded;
        EventHandler<EntityCollectionChangedEventArgs<TEntity>> comparand;
        do
        {
          comparand = eventHandler;
          eventHandler = Interlocked.CompareExchange<EventHandler<EntityCollectionChangedEventArgs<TEntity>>>(ref this.EntityAdded, comparand + value, comparand);
        }
        while (eventHandler != comparand);
      }
      remove
      {
        EventHandler<EntityCollectionChangedEventArgs<TEntity>> eventHandler = this.EntityAdded;
        EventHandler<EntityCollectionChangedEventArgs<TEntity>> comparand;
        do
        {
          comparand = eventHandler;
          eventHandler = Interlocked.CompareExchange<EventHandler<EntityCollectionChangedEventArgs<TEntity>>>(ref this.EntityAdded, comparand - value, comparand);
        }
        while (eventHandler != comparand);
      }
    }

    public event EventHandler<EntityCollectionChangedEventArgs<TEntity>> EntityRemoved
    {
      add
      {
        EventHandler<EntityCollectionChangedEventArgs<TEntity>> eventHandler = this.EntityRemoved;
        EventHandler<EntityCollectionChangedEventArgs<TEntity>> comparand;
        do
        {
          comparand = eventHandler;
          eventHandler = Interlocked.CompareExchange<EventHandler<EntityCollectionChangedEventArgs<TEntity>>>(ref this.EntityRemoved, comparand + value, comparand);
        }
        while (eventHandler != comparand);
      }
      remove
      {
        EventHandler<EntityCollectionChangedEventArgs<TEntity>> eventHandler = this.EntityRemoved;
        EventHandler<EntityCollectionChangedEventArgs<TEntity>> comparand;
        do
        {
          comparand = eventHandler;
          eventHandler = Interlocked.CompareExchange<EventHandler<EntityCollectionChangedEventArgs<TEntity>>>(ref this.EntityRemoved, comparand - value, comparand);
        }
        while (eventHandler != comparand);
      }
    }

    event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
    {
      add
      {
        this._collectionChangedEventHandler = this._collectionChangedEventHandler + value;
      }
      remove
      {
        this._collectionChangedEventHandler = this._collectionChangedEventHandler - value;
      }
    }

    event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
    {
      add
      {
        this._propertyChangedEventHandler = this._propertyChangedEventHandler + value;
      }
      remove
      {
        this._propertyChangedEventHandler = this._propertyChangedEventHandler - value;
      }
    }

    public EntityCollection(Entity parent, string memberName, Func<TEntity, bool> entityPredicate)
    {
      if (parent == null)
        throw new ArgumentNullException("parent");
      if (string.IsNullOrEmpty(memberName))
        throw new ArgumentNullException("memberName");
      if (entityPredicate == null)
        throw new ArgumentNullException("entityPredicate");
      this._parent = parent;
      this._entityPredicate = entityPredicate;
      PropertyInfo property = this._parent.GetType().GetProperty(memberName, MetaType.MemberBindingFlags);
      if (property == null)
      {
        throw new ArgumentException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resource.Property_Does_Not_Exist, new object[2]
        {
          (object) parent.GetType(),
          (object) memberName
        }), "memberName");
      }
      else
      {
        this._assocAttribute = Enumerable.SingleOrDefault<AssociationAttribute>(Enumerable.OfType<AssociationAttribute>((IEnumerable) property.GetCustomAttributes(false)));
        if (this._assocAttribute == null)
        {
          throw new ArgumentException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resource.MemberMustBeAssociation, new object[1]
          {
            (object) memberName
          }), "memberName");
        }
        else
        {
          this._isComposition = Enumerable.Any<object>((IEnumerable<object>) property.GetCustomAttributes(typeof (CompositionAttribute), false));
          this._parent.RegisterSetChangedCallback(new Action(this.OnEntitySetChanged));
          this._parent.PropertyChanged += new PropertyChangedEventHandler(this.ParentEntityPropertyChanged);
        }
      }
    }

    public EntityCollection(Entity parent, string memberName, Func<TEntity, bool> entityPredicate, Action<TEntity> attachAction, Action<TEntity> detachAction)
      : this(parent, memberName, entityPredicate)
    {
      if (attachAction == null)
        throw new ArgumentNullException("attachAction");
      if (detachAction == null)
        throw new ArgumentNullException("detachAction");
      this._attachAction = attachAction;
      this._detachAction = detachAction;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
      return (IEnumerator) this.GetEnumerator();
    }

    public void Add(TEntity entity)
    {
      if ((object) entity == null)
        throw new ArgumentNullException("entity");
      if (this.IsSourceExternal)
        throw new InvalidOperationException(Resource.EntityCollection_ModificationNotAllowedForExternalReference);
      if ((object) entity == (object) this._attachingEntity)
        return;
      if (this.SourceSet != null)
        this.SourceSet.EntityContainer.CheckCrossContainer((Entity) entity);
      this.Attach(entity);
      if (!this.Entities.Contains(entity))
      {
        bool flag = false;
        if (this.SourceSet != null)
        {
          if (!this.SourceSet.IsAttached((Entity) entity))
          {
            entity.IsInferred = true;
            this.SourceSet.Add((Entity) entity);
            flag = true;
          }
          else if (this._isComposition && entity.EntityState == EntityState.Deleted)
          {
            this.SourceSet.Add((Entity) entity);
            flag = true;
          }
        }
        if (!flag || !this.Entities.Contains(entity))
        {
          this.AddEntity(entity);
          this.RaiseCollectionChangedNotification(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (object) entity, this.Entities.Count - 1));
        }
        this._entitiesAdded = true;
      }
      this.Load();
      if (!this._isComposition)
        return;
      entity.Parent.OnChildUpdate();
    }

    public void Remove(TEntity entity)
    {
      if ((object) entity == null)
        throw new ArgumentNullException("entity");
      if ((object) entity == (object) this._detachingEntity)
        return;
      int index = this.Entities.IndexOf(entity);
      if (index == -1 && !this._entityPredicate(entity))
        throw new InvalidOperationException(Resource.Entity_Not_In_Collection);
      if (this.IsSourceExternal)
        throw new InvalidOperationException(Resource.EntityCollection_ModificationNotAllowedForExternalReference);
      this.Detach(entity);
      if (index != -1 && this.Entities.Remove(entity))
        this.RaiseCollectionChangedNotification(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, (object) entity, index));
      if (!this._isComposition)
        return;
      if (this._sourceSet != null && this._sourceSet.IsAttached((Entity) entity))
        this._sourceSet.Remove((Entity) entity);
      entity.Parent.OnChildUpdate();
    }

    public override string ToString()
    {
      return typeof (TEntity).Name;
    }

    private void AddEntity(TEntity entity)
    {
      this.Entities.Add(entity);
      if (!this._isComposition)
        return;
      entity.SetParent(this._parent, this._assocAttribute);
    }

    private void Attach(TEntity entity)
    {
      if (this._attachAction == null)
        return;
      TEntity entity1 = this._attachingEntity;
      this._attachingEntity = entity;
      try
      {
        this._attachAction(entity);
      }
      finally
      {
        this._attachingEntity = entity1;
      }
    }

    private void Detach(TEntity entity)
    {
      if (this._detachAction == null)
        return;
      TEntity entity1 = this._detachingEntity;
      this._detachingEntity = entity;
      try
      {
        this._detachAction(entity);
      }
      finally
      {
        this._detachingEntity = entity1;
      }
    }

    private void Load()
    {
      if (this._parent.EntitySet == null || this._entitiesLoaded)
        return;
      foreach (TEntity entity in Enumerable.Where<TEntity>(Enumerable.OfType<TEntity>((IEnumerable) this._parent.EntitySet.EntityContainer.GetEntitySet(typeof (TEntity))), new Func<TEntity, bool>(this.Filter)))
      {
        if (!this.Entities.Contains(entity))
          this.AddEntity(entity);
      }
      this._entitiesLoaded = true;
      this.MonitorEntitySet();
    }

    private bool Filter(TEntity entity)
    {
      if (entity.EntityState != EntityState.New)
        return this._entityPredicate(entity);
      else
        return false;
    }

    private void ParentEntityPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
      if (!this._entitiesLoaded || !Enumerable.Contains<string>(this._assocAttribute.get_ThisKeyMembers(), e.PropertyName))
        return;
      this.ResetLoadedEntities();
    }

    public IEnumerator<TEntity> GetEnumerator()
    {
      this.Load();
      return (IEnumerator<TEntity>) Enumerable.ToList<TEntity>((IEnumerable<TEntity>) this.Entities).GetEnumerator();
    }

    private void OnEntitySetChanged()
    {
      if (this._parent.EntitySet != null && this._sourceSet == null)
        this._entitiesLoaded = false;
      this.MonitorEntitySet();
    }

    private void MonitorEntitySet()
    {
      if (this._parent.EntitySet != null)
      {
        if (!this._entitiesAdded && !this._entitiesLoaded)
          return;
        if (this._sourceSet != null)
        {
          this._sourceSet.CollectionChanged -= new NotifyCollectionChangedEventHandler(this.SourceSet_CollectionChanged);
          this._sourceSet.RegisterAssociationCallback(this._assocAttribute, new Action<Entity>(this.OnEntityAssociationUpdated), false);
        }
        this._sourceSet = this._parent.EntitySet.EntityContainer.GetEntitySet(typeof (TEntity));
        this._sourceSet.CollectionChanged += new NotifyCollectionChangedEventHandler(this.SourceSet_CollectionChanged);
        this._sourceSet.RegisterAssociationCallback(this._assocAttribute, new Action<Entity>(this.OnEntityAssociationUpdated), true);
      }
      else
      {
        if (this._parent.EntitySet != null || this._sourceSet == null)
          return;
        this._sourceSet.CollectionChanged -= new NotifyCollectionChangedEventHandler(this.SourceSet_CollectionChanged);
        this._sourceSet.RegisterAssociationCallback(this._assocAttribute, new Action<Entity>(this.OnEntityAssociationUpdated), false);
        this._sourceSet = (EntitySet) null;
      }
    }

    private void OnEntityAssociationUpdated(Entity entity)
    {
      if (entity == (object) this._attachingEntity || entity == (object) this._detachingEntity || entity.EntityState == EntityState.New && entity.IsMergingState)
        return;
      TEntity entity1 = entity as TEntity;
      if ((object) entity1 == null || !this._entitiesLoaded)
        return;
      bool flag = this.Entities.Contains(entity1);
      if (!flag && this._parent.EntityState != EntityState.New && this.Filter(entity1))
      {
        this.AddEntity(entity1);
        this.RaiseCollectionChangedNotification(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (object) entity, this.Entities.Count - 1));
      }
      else
      {
        if (!flag || this._entityPredicate(entity1))
          return;
        int index = this.Entities.IndexOf(entity1);
        this.Entities.Remove(entity1);
        this.RaiseCollectionChangedNotification(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, (object) entity, index));
      }
    }

    private void SourceSet_CollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
    {
      if (this._parent.EntityState != EntityState.New && args.Action == NotifyCollectionChangedAction.Add)
      {
        TEntity[] entityArray = Enumerable.ToArray<TEntity>(Enumerable.Where<TEntity>(Enumerable.OfType<TEntity>((IEnumerable) args.NewItems), new Func<TEntity, bool>(this.Filter)));
        if (entityArray.Length <= 0)
          return;
        int index = -1;
        List<object> list = new List<object>();
        foreach (TEntity entity in entityArray)
        {
          index = this.Entities.Count;
          if (!this.Entities.Contains(entity))
          {
            this.AddEntity(entity);
            list.Add((object) entity);
          }
        }
        if (list.Count <= 0)
          return;
        this.RaiseCollectionChangedNotification(new NotifyCollectionChangedEventArgs(args.Action, Enumerable.Single<object>((IEnumerable<object>) list), index));
      }
      else if (args.Action == NotifyCollectionChangedAction.Remove)
      {
        TEntity[] entityArray = Enumerable.ToArray<TEntity>(Enumerable.Where<TEntity>(Enumerable.OfType<TEntity>((IEnumerable) args.OldItems), (Func<TEntity, bool>) (p => this.Entities.Contains(p))));
        if (entityArray.Length <= 0)
          return;
        int index = this.Entities.IndexOf(entityArray[0]);
        foreach (TEntity entity in entityArray)
          this.Entities.Remove(entity);
        this.RaiseCollectionChangedNotification(new NotifyCollectionChangedEventArgs(args.Action, (object) Enumerable.Single<TEntity>((IEnumerable<TEntity>) entityArray), index));
      }
      else
      {
        if (args.Action != NotifyCollectionChangedAction.Reset || !this._entitiesLoaded)
          return;
        this.ResetLoadedEntities();
      }
    }

    private void RaiseCollectionChangedNotification(NotifyCollectionChangedEventArgs args)
    {
      if (args.Action == NotifyCollectionChangedAction.Add)
      {
        if (this.EntityAdded != null)
        {
          foreach (TEntity entity in Enumerable.OfType<TEntity>((IEnumerable) args.NewItems))
            this.EntityAdded((object) this, new EntityCollectionChangedEventArgs<TEntity>(entity));
        }
      }
      else if (args.Action == NotifyCollectionChangedAction.Remove && this.EntityRemoved != null)
      {
        foreach (TEntity entity in Enumerable.OfType<TEntity>((IEnumerable) args.OldItems))
          this.EntityRemoved((object) this, new EntityCollectionChangedEventArgs<TEntity>(entity));
      }
      if (this._collectionChangedEventHandler != null)
        this._collectionChangedEventHandler((object) this, args);
      if (this._propertyChangedEventHandler == null)
        return;
      this._propertyChangedEventHandler((object) this, new PropertyChangedEventArgs("Count"));
    }

    private void ResetLoadedEntities()
    {
      IEnumerable<TEntity> source = (IEnumerable<TEntity>) this.Entities;
      this._entities = Enumerable.ToList<TEntity>(Enumerable.Where<TEntity>((IEnumerable<TEntity>) this.Entities, (Func<TEntity, bool>) (p => p.EntityState == EntityState.New)));
      this._entitiesLoaded = false;
      if (this.EntityRemoved != null)
      {
        foreach (TEntity entity in Enumerable.Where<TEntity>(source, (Func<TEntity, bool>) (p => !this._entities.Contains(p))))
          this.EntityRemoved((object) this, new EntityCollectionChangedEventArgs<TEntity>(entity));
      }
      this.RaiseCollectionChangedNotification(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    void IEntityCollection.Add(Entity entity)
    {
      this.Add((TEntity) entity);
    }

    void IEntityCollection.Remove(Entity entity)
    {
      this.Remove((TEntity) entity);
    }

    ICollectionView ICollectionViewFactory.CreateView()
    {
      return new CollectionViewSource()
      {
        Source = ((object) new EntityCollection<TEntity>.ListCollectionViewProxy<TEntity>(this))
      }.View;
    }

    private class ListCollectionViewProxy<T> : IList, ICollection, IEnumerable<T>, IEnumerable, INotifyCollectionChanged, ICollectionChangedListener where T : Entity
    {
      private readonly object _syncRoot = new object();
      private readonly List<T> _addedEntities = new List<T>();
      private readonly EntityCollection<T> _source;
      private readonly WeakCollectionChangedListener _weakCollectionChangedLister;
      private NotifyCollectionChangedEventHandler CollectionChanged;

      public bool IsFixedSize
      {
        get
        {
          return false;
        }
      }

      public bool IsReadOnly
      {
        get
        {
          return false;
        }
      }

      public object this[int index]
      {
        get
        {
          if (index < 0 || index >= this.Source.Count)
            return (object) null;
          else
            return (object) this.Source.Entities[index];
        }
        set
        {
          throw new NotSupportedException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resource.IsNotSupported, new object[1]
          {
            (object) "Indexed setting"
          }));
        }
      }

      public int Count
      {
        get
        {
          return this.Source.Count;
        }
      }

      public bool IsSynchronized
      {
        get
        {
          return false;
        }
      }

      public object SyncRoot
      {
        get
        {
          return this._syncRoot;
        }
      }

      EntityCollection<T> Source
      {
        private get
        {
          return this._source;
        }
      }

      public event NotifyCollectionChangedEventHandler CollectionChanged
      {
        add
        {
          NotifyCollectionChangedEventHandler changedEventHandler = this.CollectionChanged;
          NotifyCollectionChangedEventHandler comparand;
          do
          {
            comparand = changedEventHandler;
            changedEventHandler = Interlocked.CompareExchange<NotifyCollectionChangedEventHandler>(ref this.CollectionChanged, comparand + value, comparand);
          }
          while (changedEventHandler != comparand);
        }
        remove
        {
          NotifyCollectionChangedEventHandler changedEventHandler = this.CollectionChanged;
          NotifyCollectionChangedEventHandler comparand;
          do
          {
            comparand = changedEventHandler;
            changedEventHandler = Interlocked.CompareExchange<NotifyCollectionChangedEventHandler>(ref this.CollectionChanged, comparand - value, comparand);
          }
          while (changedEventHandler != comparand);
        }
      }

      internal ListCollectionViewProxy(EntityCollection<T> source)
      {
        this._source = source;
        this._weakCollectionChangedLister = WeakCollectionChangedListener.CreateIfNecessary((object) this._source, (ICollectionChangedListener) this);
      }

      public int Add(object value)
      {
        T entity = value as T;
        if ((object) entity == null)
        {
          throw new ArgumentException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resource.MustBeAnEntity, new object[1]
          {
            (object) "value"
          }), "value");
        }
        else
        {
          this._addedEntities.Add(entity);
          this.Source.Add(entity);
          return this.IndexOf((object) entity);
        }
      }

      public void Clear()
      {
        throw new NotSupportedException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resource.IsNotSupported, new object[1]
        {
          (object) "Clear"
        }));
      }

      public bool Contains(object value)
      {
        return this.IndexOf(value) >= 0;
      }

      public int IndexOf(object value)
      {
        return this.Source.Entities.IndexOf(value);
      }

      public void Insert(int index, object value)
      {
        throw new NotSupportedException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resource.IsNotSupported, new object[1]
        {
          (object) "Insert"
        }));
      }

      public void Remove(object value)
      {
        T entity = value as T;
        if ((object) entity == null)
          return;
        this.Source.Remove(entity);
        if (!this._addedEntities.Contains(entity))
          return;
        this._addedEntities.Remove(entity);
        this.Source.SourceSet.Remove((Entity) entity);
      }

      public void RemoveAt(int index)
      {
        this.Remove(this[index]);
      }

      public void CopyTo(Array array, int index)
      {
        this.Source.Entities.CopyTo(array, index);
      }

      public IEnumerator<T> GetEnumerator()
      {
        return this.Source.GetEnumerator();
      }

      IEnumerator IEnumerable.GetEnumerator()
      {
        return (IEnumerator) this.GetEnumerator();
      }

      private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
      {
        NotifyCollectionChangedEventHandler changedEventHandler = this.CollectionChanged;
        if (changedEventHandler == null)
          return;
        changedEventHandler((object) this, e);
      }

      private void OnSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
      {
        this.OnCollectionChanged(e);
      }

      void ICollectionChangedListener.OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
      {
        this.OnSourceCollectionChanged(sender, e);
      }
    }
  }
}
